package net.hn.hnms.biz.activiti.controller;

import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName SignaturePictureProcessUtil
 * @Description 处理签名图片（二值化处理并切割）
 * @Author halo
 * @Date 2024/1/4 10:30
 **/
public class SignaturePictureProcessUtil {

    // 横向切分块数
    private static final int widthSplitNum = 12;

    // 纵向切分块数
    private static final int heightSplitNum = 14;

    /**
     * 处理签名图片
     *
     * @throws IOException
     */
    public static void signaturePictureProcess(String picPath) throws IOException {
        File file = new File(picPath);
        BufferedImage bufferedImage = ImageIO.read(file);

        //压缩图片
        BufferedImage compactImage = Thumbnails.of(bufferedImage).size(2000, 2000).asBufferedImage();

        // 切割图片，分别处理，防止出现大片黑斑
        Map<Integer, BufferedImage> map = splitImage(compactImage);

        // 二值化
        for (Integer key : map.keySet()) {
            BufferedImage binaryImage = binaryImage(map.get(key));
            map.put(key, binaryImage);
        }

        //合并图片
        BufferedImage destImage = mergeImage(map);
        BufferedImage image = cutLR(destImage);

        BufferedImage resultImage = Thumbnails.of(image).size(200, 200).asBufferedImage();
        // 黑白图片输出目录
        File newFile = new File(picPath);
        ImageIO.write(resultImage, "jpg", newFile);
    }

    /**
     * 处理签名图片
     *
     * @throws IOException
     */
    public static BufferedImage signaturePictureProcess(BufferedImage bufferedImage) throws IOException {


        //压缩图片
        BufferedImage compactImage = Thumbnails.of(bufferedImage).size(2000, 2000).asBufferedImage();

        // 切割图片，分别处理，防止出现大片黑斑
        Map<Integer, BufferedImage> map = splitImage(compactImage);

        // 二值化
        for (Integer key : map.keySet()) {
            BufferedImage binaryImage = binaryImage(map.get(key));
            map.put(key, binaryImage);
        }

        //合并图片
        BufferedImage destImage = mergeImage(map);
        BufferedImage image = cutLR(destImage);

        BufferedImage resultImage = Thumbnails.of(image).size(200, 200).asBufferedImage();
        //设置背景颜色  无效
//        Color backgroundColor = Color.RED; // 背景颜色
//        Graphics2D graphics = bufferedImage.createGraphics();
//        graphics.setColor(backgroundColor);
//        graphics.fillRect(0, 0, resultImage.getWidth(), resultImage.getHeight());
        return resultImage;

    }


    /**
     * 合并多张签名为一张
     *
     * @param imageList
     * @return BufferedImage
     * @throws IOException
     */
    public static BufferedImage mergeImages(List<BufferedImage> imageList) throws IOException {
        Map<Integer, BufferedImage> map = new HashMap<>();
        int blockWith = 0;
        int blockHeight = 0;

        for (int i = 0; i < imageList.size(); i++) {
            BufferedImage bufferedImage = imageList.get(i);
            blockWith = bufferedImage.getWidth() > blockWith ? bufferedImage.getWidth() : blockWith;
            blockHeight = bufferedImage.getHeight() > blockHeight ? bufferedImage.getHeight() : blockHeight;
            map.put(i, bufferedImage);
        }

        BufferedImage destImage = mergeImage(map, blockWith, blockHeight);

        // 获取 resources 目录下的文件路径
//        URL resourceUrl = SignaturePictureProcessUtil.class.getClassLoader().getResource("");

//        String filePath = WebConfig.SWYT_FILE_UPLOAD_DIR + "/signature.png";
        // 黑白图片输出目录
        // 获取用户目录
//        String userDir = System.getProperty("user.home");
//        // 构建保存文件的路径
//        String filePath = userDir + File.separator + "image.png";
//        File newFile = new File(filePath);
//        ImageIO.write(destImage, "png", newFile);
        return destImage;
    }
    /**
     * 切分图片
     *
     * @param bufferedImage
     * @return
     */
    private static Map<Integer, BufferedImage> splitImage(BufferedImage bufferedImage) {
        HashMap<Integer, BufferedImage> map = new HashMap<>();

        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int type = bufferedImage.getType();

        int blockWidth = w / widthSplitNum;
        int blockHeight = h / heightSplitNum;

        for (int i = 0; i < widthSplitNum; i++) {
            for (int j = 0; j < heightSplitNum; j++) {
                BufferedImage grayImage = new BufferedImage(blockWidth, blockHeight, type);

                int mStart = i * blockWidth;
                int mEnd = (i + 1) * blockWidth;
                int nStart = j * blockHeight;
                int nEnd = (j + 1) * blockHeight;

                for (int m = mStart; m < mEnd; m++) {
                    for (int n = nStart; n < nEnd; n++) {
                        grayImage.setRGB(m - mStart, n - nStart, bufferedImage.getRGB(m, n));
                    }
                }
                map.put(i * heightSplitNum + j, grayImage);
            }
        }
        return map;
    }

    /**
     * 二值化图片
     *
     * @param bufferedImage 原图片
     * @return 二值化后的图片
     */
    private static BufferedImage binaryImage(BufferedImage bufferedImage) {
        BufferedImage grayImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), bufferedImage.getType());

        int threshold = getMeanThreshold(bufferedImage);

        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                // getRGB()方法，根据手册，其返回的int型数据（32位）为ARGB格式，其中ARGB各占8bit
                int color = bufferedImage.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
                if (gray > threshold) {
                    // 白色
                    grayImage.setRGB(i, j, 0xFFFFFF);
                } else {
                    //黑色
                    grayImage.setRGB(i, j, 0);
                }
            }
        }

        return grayImage;
    }

    /**
     * 获取图片的阈值，采用基于灰度平均值的阈值
     *
     * @param bufferedImage 原图片
     * @return 二值化的阈值
     */
    private static int getMeanThreshold(BufferedImage bufferedImage) {

        double aa = 0.83;
        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int num = 0;
        int sum = 0;

        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                int color = bufferedImage.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;

                int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
                sum += gray;
                num += 1;
            }
        }

        //测试表明，阈值取平局值的1.2倍效果最好
        //越大越黑
        int threshold = (int) (sum / num);

        if (threshold * aa < 255) {
            threshold = (int) (aa * sum / num);
        }
        return threshold;
    }

    /**
     * 合并图片
     *
     * @param map
     * @return
     */
    private static BufferedImage mergeImage(Map<Integer, BufferedImage> map) {
        if (map == null || map.get(0) == null) {
            return null;
        }
        int blockWidth = map.get(0).getWidth();
        int blockHeight = map.get(0).getHeight();

        BufferedImage destImage = new BufferedImage(blockWidth * widthSplitNum, blockHeight * heightSplitNum, BufferedImage.TYPE_BYTE_BINARY);

        for (int i = 0; i < widthSplitNum; i++) {
            for (int j = 0; j < heightSplitNum; j++) {
                BufferedImage grayImage = map.get(i * heightSplitNum + j);

                for (int m = 0; m < grayImage.getWidth(); m++) {
                    for (int n = 0; n < grayImage.getHeight(); n++) {
                        destImage.setRGB(i * blockWidth + m, j * blockHeight + n, grayImage.getRGB(m, n));
                    }
                }
            }
        }
        return destImage;
    }

    /**
     * 切取截图
     *
     * @param bufferedImage
     * @return
     */
    private static BufferedImage cutLR(BufferedImage bufferedImage) {
        //当一行或一列黑色元素个数小于5时，认为是污点
        int ignoreSmudge = 5;

        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();

        int leftW = 0;
        int rightW = 0;
        int upH = 0;
        int downH = 0;

        //获取最右坐标点坐标
        for (int i = 0; i < width; i++) {
            int blackCount = 0;
            for (int j = 0; j < height; j++) {
                int color = bufferedImage.getRGB(i, j);
                int b = color & 0xff;
                if (b == 0) {
                    blackCount++;
                    if (blackCount > ignoreSmudge) {
                        rightW = i;
                        break;
                    }
                }
            }
        }
        rightW = rightW + 10 <= width ? rightW + 10 : width;
        //获取最左坐标点坐标
        for (int i = width - 1; i > 0; i--) {
            int blackCount = 0;
            for (int j = 0; j < height; j++) {
                int color = bufferedImage.getRGB(i, j);
                int b = color & 0xff;
                if (b == 0) {
                    blackCount++;
                    if (blackCount > ignoreSmudge) {
                        leftW = i;
                        break;
                    }
                }
            }
        }
        leftW = leftW - 10 >= 0 ? leftW - 10 : 0;
        // 获取最下坐标点坐标
        for (int i = 0; i < height; i++) {
            int blackCount = 0;
            for (int j = 0; j < width; j++) {
                int color = bufferedImage.getRGB(j, i);
                int b = color & 0xff;
                if (b == 0) {
                    blackCount++;
                    if (blackCount > ignoreSmudge) {
                        downH = i;
                        break;
                    }
                }
            }
        }
        downH = downH + 10 <= height ? downH + 10 : height;
        //获取最上坐标点坐标
        for (int i = height - 1; i > 0; i--) {
            int blackCount = 0;
            for (int j = 0; j < width; j++) {
                int color = bufferedImage.getRGB(j, i);
                int b = color & 0xff;
                if (b == 0) {
                    blackCount++;
                    if (blackCount > ignoreSmudge) {
                        upH = i;
                        break;
                    }
                }
            }
        }
        upH = upH - 10 >= 0 ? upH - 10 : 0;
        //截取图片
        return bufferedImage.getSubimage(leftW, upH, rightW - leftW, downH - upH);
    }

    /**
     * 合并图片
     *
     * @param map
     * @param blockWidth
     * @param blockHeight
     * @return
     */
    private static BufferedImage mergeImage(Map<Integer, BufferedImage> map, int blockWidth, int blockHeight) {
        if (map == null || map.get(0) == null) {
            return null;
        }

        blockWidth = blockWidth + 35;
        blockHeight = blockHeight + 35;
        int widthNum = 8;
        widthNum = map.size() < widthNum ? map.size() : widthNum;
        int heightNum = map.size() / widthNum + 1;
        BufferedImage destImage = new BufferedImage(blockWidth * widthNum, blockHeight * heightNum, BufferedImage.TYPE_BYTE_BINARY);

        int num = 0;
        for (int j = 0; j < heightNum; j++) {
            for (int i = 0; i < widthNum; i++) {
                if (num < map.size()) {
                    BufferedImage grayImage = map.get(num);

                    for (int m = 0; m < blockWidth; m++) {
                        if (m < grayImage.getWidth()) {
                            for (int n = 0; n < blockHeight; n++) {
                                if (n < grayImage.getHeight()) {
                                    destImage.setRGB(i * blockWidth + m, j * blockHeight + n, grayImage.getRGB(m, n));
                                } else {
                                    destImage.setRGB(i * blockWidth + m, j * blockHeight + n, 0xFFFFFF);
                                }
                            }
                        } else {
                            for (int n = 0; n < blockHeight; n++) {
                                destImage.setRGB(i * blockWidth + m, j * blockHeight + n, 0xFFFFFF);
                            }
                        }
                    }
                } else {
                    for (int m = 0; m < blockWidth; m++) {
                        for (int n = 0; n < blockHeight; n++) {
                            destImage.setRGB(i * blockWidth + m, j * blockHeight + n, 0xFFFFFF);
                        }
                    }
                }
                num++;
            }
        }
        return destImage;
    }
}
